package me.yangbajing.model

import scala.collection.mutable

import yangbajing.persistence.SquerylEntrypoint._
import yangbajing.util.Imports.Y

import org.squeryl.{Session, KeyedEntity}
import org.squeryl.dsl.CompositeKey2

import net.liftweb.common.{Loggable, Box}

import me.yangbajing.common.Tag
import yangbajing.util.Pagination
import java.sql.SQLException

object MTag extends Loggable {

  def findOne(tagId: String): Box[Tag] =
    Y.tryBox {
      require(Y.nonBlank(tagId), "标签ID不能为空")
      val sql = """select t.tag_id, count(t.ref_id) tag_count
                  | from tag_ t
                  | where tag_id = ?
                  | group by t.tag_id"""

      transaction {
        val conn = Session.currentSession.connection
        val pstmt = conn.prepareStatement(sql)
        pstmt.setString(1, tagId)
        val rs = pstmt.executeQuery()
        val result =
          if (rs.next()) Tag(rs.getString("tag_id"), rs.getInt("tag_count"))
          else throw new SQLException("数据不存在")

        if (rs.next()) throw new SQLException("数据多于一行")

        result
      }
    }

  def findTagIdsByRefId(refId: String): List[String] =
    transaction(from(Entities.tags)(t => where(t.ref_id === refId) select t.tag_id).toList)

  def insertFormRef(refId: String, tags: Iterable[String]) =
    inTransaction {
      val vs = tags.map(tagId => MTag(tagId, refId))
      Entities.tags insert vs
    }

  def removeFromRef(refId: String, tags: Iterable[String]) =
    inTransaction(
      Entities.tags.deleteWhere(t => t.ref_id === refId and (t.tag_id in tags))
    )

  def insert(tagId: String, refId: String): Box[_] = Y.tryBox {
    transaction(Entities.tags insert MTag(tagId, refId))
  }

  def size(refId: Option[String] = None): Long =
    transaction(from(Entities.tags)(t =>
      where(
        t.ref_id === refId.?
      ) compute countDistinct(t.tag_id)))

  def findAll(offset: Int, limit: Int, refId: Option[String] = None): List[Tag] =
    transaction {
      val conn = Session.currentSession.connection
      val _sql = """select t.tag_id, count(t.ref_id) tag_count
                   | from tag_ t
                   | %s
                   | group by t.tag_id
                   | order by tag_count desc
                   | offset ? limit ?""".stripMargin
      val sql = _sql format (if (refId.isDefined) "where ref_id = ? " else "")

      logger.debug("findAll sql: " + sql)

      val pstmt = conn.prepareStatement(sql)
      var idx = 1
      for (v <- refId) {
        pstmt.setString(idx, v)
        idx += 1
      }
      pstmt.setInt(idx, offset)
      idx += 1
      pstmt.setInt(idx, limit)

      val buffer = mutable.Buffer[Tag]()
      val rs = pstmt.executeQuery()
      while (rs.next())
        buffer += Tag(rs.getString("tag_id"), rs.getInt("tag_count"))

      buffer.toList
    }

  def pagination(_curPage: Int, _limit: Int,
                 refId: Option[String] = None) =
    new Pagination[Tag] {
      val curPage = _curPage
      val limit = _limit
      val total = size(refId = refId)
      val page = findAll(offset, limit, refId = refId)
    }

  def remove(tagId: String, refId: String): Int =
    transaction(Entities.tags.deleteWhere(t => t.tag_id === tagId and t.ref_id === refId))
}

case class MTag(
                 tag_id: String,
                 ref_id: String) extends KeyedEntity[CompositeKey2[String, String]] {
  def id = compositeKey(tag_id, ref_id)
}
